<?php
/*
PPS: Php Script - Ver 0.0.6
https://gitee.com/wsemad/pps.git
作者：加加米；邮箱：plusplusmi@163.com
*/

class pps
{
	/// 所有类定义至此，支持重复定义，撤销定义
	public static $class = array();

	/// 双分派表
	/// [
	/// 频道(c) => [
	///			   类名(t) => [
	///					      类名(t) => 函数(f),
	///					      ...
	///					      ],
	///			   ...
	///			   ],
	///	...
	/// ]
	public static $dpcttf = array();

	/// 支持以“pps::MyClass()”语法创建类实例
	public static function __callStatic($type, $agms)
	{
		if (!isset(pps::$class[$type]))
		{ throw new \Exception('类"' . $type . '"未定义', -1); }
		$ctor = pps::$class[$type]->__lambda;
		return $agms ? call_user_func_array($ctor, $agms) : call_user_func($ctor);
	}

	/// 类版本？与pps::class()配合使用，仅当返回false时才重定义类，用法见 test.php
	public static function class_ver($type, $ver)
	{
		pps::$_cv_type = $type; pps::$_cv_ver = $ver;
		return isset(pps::$class[$type]) && (pps::$class[$type]->__ver === $ver);
	}
	public static $_cv_type = ''; // 这俩变量仅由本类内部使用
	public static $_cv_ver = '';

	/// 文件修改？热更新代码时，与pps::class()配合使用，仅当返回true时才重定义类
	/// prod: int，检查周期（秒），仅当距离上一次检查达到或超过指定秒数后才再次检查，默认0表示立即检查
	///		【说明：因为每次检查文件修改时间也是一次设备IO操作，可用该参数减轻IO压力】
	public static function file_mod($path, $prod = 0)
	{
		if (($prod > 0) && pps::$_fmt_map && isset(pps::$_fmt_map[$path]) && (time() - pps::$_fmt_map[$path] < $prod)) { return false; }
		clearstatcache(true, $path); $mt = filemtime($path); // 注意先清除缓存
		if (pps::$_fm_map && isset(pps::$_fm_map[$path]) && (pps::$_fm_map[$path] === $mt)) { return false; }
		pps::$_fm_map[$path] = $mt;
		if ($prod > 0) { pps::$_fmt_map[$path] = time(); }
		return true;
	}
	public static $_fm_map = null;
	public static $_fmt_map = null;

	/// 运行期用户变量
	public static $vars = array();

	/// 在$vars中按需定义值
	public static function vars_val($k, $v)
	{
		if (!isset(pps::$vars[$k])) { pps::$vars[$k] = $v; }
	}

	/// 在$vars中按需定义数组
	public static function vars_array($k)
	{
		if (!isset(pps::$vars[$k])) { pps::$vars[$k] = array(); }
	}


	/// 运行脚本
	/// main: str，入口函数名，即$_main_map的键，指向一个匿名函数
	/// args: array，参数集
	/// path: str，脚本路径，该脚本的最后一步应向pps::$_main_map注册一个匿名函数
	/// prod: 见file_mod()
	public static function run($main, $args, $path, $prod = 0)
	{
		if (pps::file_mod($path, $prod))
		{ include($path); }
		if (!pps::$_main_map || !isset(pps::$_main_map[$main]))
		{ throw new \Exception('入口函数"' . $main . '"未注册'); }
		return (pps::$_main_map[$main])($args);
	}
	public static $_main_map = null;

	/// 定义类
	/// type: str，类名
	/// base: str，基类名，null/"" 表示不继承
	/// mthds: [=>]，方法映射，键是方法名，值是函数或高级设置，详见 test.php
	public static function class($type, $base = null, $mthds = array())
	{
		if (!$type) { $type = pps::$_cv_type; }
		else { pps::$_cv_type = $type; pps::$_cv_ver = ''; }

		// 如果有基类，确保已定义，且派生类的非虚函数不会覆盖基类的同名函数
		if ($base)
		{
			if (!isset(pps::$class[$base]))
			{ throw new \Exception('类"' . $type . '"的基类"' . $base . '"未定义', -1); }

			foreach ($mthds as $mthd => $_) 
			{
				if (('__init' === $mthd) || ('virtual ' === substr($mthd, 0, 8))) 
				{ continue; }
				
				$c = pps::$class[$base];
				while ($c && !isset($c->__methods[$mthd]))
				{ $c = $c->__base ? pps::$class[$c->__base] : null; }
				if ($c)
				{ throw new \Exception('类"' . $type . '"的非虚方法"' . $mthd . '"覆盖了祖先类"' . $c->__type . '"的同名方法', -1); }
			}
		}

		// 类就是一个ppsfun对象，调用它等价于新建类的实例
		$c = new ppsfun(function ($a___ = null) use ($type)
		{ return new ppsobj(pps::$class[$type], func_get_args()); });
		$c->__type = $type; // 类名
		$c->__ver = pps::$_cv_ver; // 版本
		$c->__base = $base; // 类的基类名
		$c->__methods = array(); // 添加方法
		foreach ($mthds as $mthd => $m) 
		{ pps::_def_mthd($type, $c->__methods, $mthd, $m); }
		pps::$class[$type] = $c; // 注册类
		return $c;
	}

	/// 撤销类
	public static function declass($type)
	{ unset(pps::$class[$type]); }

	/// 有类？
	public static function has_class($type)
	{ return isset(pps::$class[$type]); }

	/// 补充定义方法，在class()调用后，可用本函数 增加/修改/删除 方法
	/// mthd: str，方法名
	/// m: 若为null表示删除，否则表示增加/修改，取值详见 test.php
	public static function method($type, $mthd, $m)
	{
		$c = pps::_chk_type_mthd($type);
		if ($m) { pps::_def_mthd($type, $c->__methods, $mthd, $m); }
		else { unset($c->__methods[$mthd]); }
	}

	/// 搜索方法所属类，未找到时返回null，找到时返回类
	/// type: str/ppsobj，类名，或类的任一实例
	/// mthd: str，方法名
	/// inherit: bool，是否继承祖先类的？默认false表示仅当type类定义mthd时才返回true
	public static function search_method($type, $mthd, $inherit = false)
	{
		if ($type instanceof ppsobj) { $type = $type->__type; }
		if (!$inherit)
		{ return pps::_chk_type_mthd($type, $mthd, false) ? pps::$class[$type] : null; }

		$c = isset(pps::$class[$type]) ? pps::$class[$type] : null;
		while ($c)
		{
			if (isset($c->__methods[$mthd])) { return $c; }
			$c = ($c->__base && isset(pps::$class[$c->__base])) ? pps::$class[$c->__base] : null;
		}
		return null;
	}

	/// 有方法？参数同search_method()，返回bool
	public static function has_method($type, $mthd, $inherit = false)
	{ return pps::search_method($type, $mthd, $inherit) ? true : false; }

	/// 在派生类方法中调用基类方法
	/// mthd: str，方法名，null/"" 表示构造函数
	public static function parent($mthd, $self, $a___ = null)
	{
		if (!$mthd) { $mthd = '__init'; } // 默认
		$stype = end($self->__callstk); // 静态类型
		$base = pps::$class[$stype]->__base;
		if (!isset(pps::$class[$base])) 
		{ throw new \Exception('类"' . $base . '"未定义', -1); }

		$c = pps::$class[$base];
		while ($c && !isset($c->__methods[$mthd]))
		{ $c = $c->__base ? pps::$class[$c->__base] : null; }
		if (!$c)
		{ throw new \Exception('类"' . $stype . '"的基类方法"' . $mthd . '()"未定义', -1); }
		$tmthd = $c->__type;
		$fmthd = $c->__methods[$mthd];
		$agms = func_get_args();
		array_shift($agms); // $mthd
		array_shift($agms); // $self
		return pps::_call_mthd($self, $mthd, $tmthd, $fmthd, $agms);
	}

	/// AOP Enter，详见 test.php
	public static function aop_enter($type, $mthd, $fun)
	{ pps::_aop_add(-1, $type, $mthd, $fun); }

	/// AOP Leave，详见 test.php
	public static function aop_leave($type, $mthd, $fun)
	{ pps::_aop_add(+1, $type, $mthd, $fun); }

	/// 新建一个ppsfun对象
	/// help: str/str[]，内联文档，一整段字符串或行数组
	/// lambda: f()，匿名函数
	/// pptys: [=>]，属性的键值对儿
	public static function fun($help, $lambda, $pptys = null)
	{
		$fun = new ppsfun($lambda, $pptys);
		if ($help) { $fun->__help = is_array($help) ? implode(PHP_EOL, $help) : $help; }
		return $fun;
	}

	/// 变量类型，仅能识别ppsobj和ppsfun，其他类型变量一概返回null
	public static function typeof($var)
	{
		if (!$var) { return null; }
		if ($var instanceof ppsobj) { return $var->__type; }
		if ($var instanceof ppsfun) { return 'ppsfun'; }
		return null;
	}

	/// 取得var的类，参数可以是ppsobj和字符串，若是""则使用“pps::_cv_type”
	public static function classof($var)
	{
		if ('' === $var) { $var = pps::$_cv_type; }
		else if ($var instanceof ppsobj) { $var = $var->__type; }
		return ($var && isset(pps::$class[$var])) ? pps::$class[$var] : null;
	}

	/// 列出所有类
	public static function list_class()
	{ $list = array_keys(pps::$class); sort($list); return $list; }

	/// 列出指定类的所有方法
	public static function list_method($type)
	{
		$c = pps::_chk_type_mthd($type);
		$list = array();
		foreach ($c->__methods as $k => $v) 
		{ if ('_' !== $k[0]) { $list[] = $k; } }
		sort($list);
		while ($c->__base && isset(pps::$class[$c->__base]))
		{
			$c = pps::$class[$c->__base];
			$blist = array();
			foreach ($c->__methods as $k => $v) 
			{ if ('_' !== $k[0]) { $blist[] = $c->__type . '::' . $k; } }
			sort($blist);
			$list = array_merge($list, $blist);
		}
		return $list;
	}

	/// 列出指定类的静态变量，跳过内部属性
	public static function list_svar($type)
	{ return pps::_list_static($type, true); }

	/// 列出指定类的静态方法，跳过内部属性
	public static function list_smethod($type)
	{ return pps::_list_static($type, false); }

	/// 列出祖先类，还可用于查找祖先类
	/// type: str/ppsobj，类名或类的实例
	/// search: str，类名，若非""，则当找到该类时返回true，未找到时返回false
	public static function list_ancestor($type, $search = '')
	{
		if ($type instanceof ppsobj) { $type = $type->__type; }
		$c = pps::_chk_type_mthd($type);
		$list = $search ? null : array(); // 类名不可能是"0"
		while ($c && $c->__base)
		{
			if ($search === $c->__base) { return true; }
			$list[] = $c->__base;
			$c = isset(pps::$class[$c->__base]) ? pps::$class[$c->__base] : null;
		}
		return $search ? false : $list;
	}

	/// <ppsobj>$obj 是 <str>$type 的实例？
	public static function instanceof($obj, $type)
	{ return ($obj->__type === $type) || pps::list_ancestor($obj->__type, $type); }

	/// 返回内联文档
	/// type: str/ppsfun，若为str则表示类名，否则视作ppsfun对象
	public static function help($type, $mthd = null)
	{
		if ($type instanceof ppsfun) { return $type->__help; }
		$c = pps::_chk_type_mthd($type, $mthd);
		if (!$mthd) { $mthd = '__init'; }
		return (is_array($c->__methods[$mthd]) && is_string($c->__methods[$mthd][0])) ? $c->__methods[$mthd][0] : null;
	}

	/// 配置双分派表
	/// f： function/ppsfun，签名：<T> f(t1 one, t2 two, ...)
	public static function dpcttf($c, $t1, $t2, $f)
	{
		if (!isset(pps::$dpcttf[$c])) { pps::$dpcttf[$c] = array(); }
		if (!isset(pps::$dpcttf[$c][$t1])) { pps::$dpcttf[$c][$t1] = array(); }
		pps::$dpcttf[$c][$t1][$t2] = $f;
	}

	/// 双分派，分派成功时返回调用函数的返回值，失败时抛出异常
	/// channel: str，频道，表示两种类型间的一种关系，对称关系以“^”开头，表示可交换 one & two 的顺序
	/// one, two: ppsobj，两个对象，根据他们的类型进行分派
	/// agms: array，参数数组，注意这里接收引用，意味着通过调用可以修改参数
	public static function dispatch($channel, $one, $two, &$agms)
	{
		// 双分派算法：
		// 1. 如果[t1][t2]存在，使用它
		// 2. 如果可交换，且[t2][t1]存在，使用它
		// 3. 沿着t1和t2的继承链，尝试找到[t1最近祖先][t2或t2最近祖先]
		// 4. 如果可交换，尝试找到[t2最近祖先][t1或t1最近祖先]，
		//			如果上一步找到了，这一步找到了，报告二义性错误
		//			如果上一步找到了，这一步未找到，使用上一步的
		//			如果上一步未找到，这一步找到了，使用这一步的
		// 5. 如果已经确定使用哪个函数，调用它，否则报告未能分派错误
		if (!isset(pps::$dpcttf[$channel])) 
		{ throw new \Exception('双分派频道"' . $channel . '"不存在', -1); }
		$dpc = &pps::$dpcttf[$channel];
		$t1 = $one->__type;
		$t2 = $two->__type;
		$swap = ('^' === $channel[0]);
		$f = null;
		if (isset($dpc[$t1]) && isset($dpc[$t1][$t2]))
		{ $f = $dpc[$t1][$t2]; }
		else
		if ($swap && isset($dpc[$t2]) && isset($dpc[$t2][$t1]))
		{ $f = $dpc[$t2][$t1]; }
		else
		{
			$one_types = pps::list_ancestor($t1);
			array_unshift($one_types, $t1);
			$one_types_len = count($one_types);
			$two_types = pps::list_ancestor($t2);
			array_unshift($two_types, $t2);
			$two_types_len = count($two_types);

			for ($j=0; $j<$two_types_len; ++$j)
			{
				$ttj1 = $two_types[$j];
				for ($i=1; $i<$one_types_len; ++$i)
				{
					$oti1 = $one_types[$i];
					if (isset($dpc[$oti1]) && isset($dpc[$oti1][$ttj1]))
					{
						$f = $dpc[$oti1][$ttj1];
						break 2;
					}
				}
			}

			if ($swap)
			{
				for ($i=0; $i<$one_types_len; ++$i)
				{
					$oti2 = $one_types[$i];
					for ($j=1; $j<$two_types_len; ++$j)
					{
						$ttj2 = $two_types[$j];		
						if (isset($dpc[$ttj2]) && isset($dpc[$ttj2][$oti2]))
						{
							if ($f) // 发现二义性，直接报错，不要自作主张！！
							{
								throw new \Exception('分派二义性: "' . $t1 . '" & "' . $t2 . '"; '
								. '选择1: "' . $oti1 . '" & "' . $ttj1 . '"; '
								. '选择2: "' . $ttj2 . '" & "' . $oti2 . '";', -1); 
							}
							$f = $dpc[$ttj2][$oti2];
							break 2;
						}
					}
				}
			}
		}

		if (!$f)
		{ throw new \Exception('未能分派: "' . $t1 . '" & "' . $t2 . '"', -1); }

		array_unshift($agms, $two); array_unshift($agms, $one);
		try { return call_user_func_array($f, $agms); }
		finally { array_shift($agms); array_shift($agms); }
	}

	//========================= 【内部函数】

	public static function _def_mthd($type, &$mthds, $mthd, &$m)
	{
		if (is_array($m) && is_array($m[0])) // 如果有内联文档，且是行数组形式
		{
			if (false !== ($p = strpos($m[0][0], '(')))
			{
				$m[0][0] =	substr($m[0][0], 0, $p) . $type . 
							(('__init' === $mthd) ? '' : ('::' . $mthd)) . substr($m[0][0], $p);
			}
			$m[0] = implode(PHP_EOL, $m[0]); // 用换行符连接成一整段字符串
		}
		$mthds[('virtual ' === substr($mthd, 0, 8)) ? substr($mthd, 8) : $mthd] = $m; // 去掉修饰符
	}

	public static function _call_mthd($self, $mthd, $tmthd, $fmthd, &$agms)
	{
		$rtn = null;
		try
		{
			$self->__callstk[] = $tmthd; // 追踪方法的静态类型
			if (is_array($fmthd))
			{
				$aopagms = array(null, $mthd, $self, null);
				$aopagms[0] = &$rtn; // 引用，允许AOP修改
				$aopagms[3] = &$agms; // 引用，允许AOP修改
				$fmthd_len = count($fmthd);
				for ($i=0; $i<$fmthd_len; ++$i)
				{
					$m = $fmthd[$i];
					if (!$i && is_string($m)) // 如果有内联文档，一定位于[0]，跳过
					{ }
					else
					if ($m instanceof ppsfun) // aop函数对象
					{ call_user_func_array($m->__lambda, $aopagms); }
					else // 方法本体
					{ pps::_call_mthd_agms($rtn, $self, $m, $agms); }
				}
			}
			else
			{ pps::_call_mthd_agms($rtn, $self, $fmthd, $agms); }
		}
		finally
		{ array_pop($self->__callstk); }
		return $rtn;
	}

	public static function _call_mthd_agms(&$rtn, $self, $fmthd, &$agms)
	{
		array_unshift($agms, $self);
		try { $rtn = call_user_func_array($fmthd, $agms); }
		finally { array_shift($agms); }
	}

	public static function _chk_type_mthd($type, $mthd = null, $throw = true)
	{
		if (!isset(pps::$class[$type]))
		{ if ($throw) { throw new \Exception('类"' . $type . '"未定义', -1); } return false; }
		$c = pps::$class[$type];
		if ($mthd && !isset($c->__methods[$mthd]))
		{ if ($throw) { throw new \Exception('类"' . $type . '"的方法"' . $mthd . '()"未定义', -1); } return false; }
		return $throw ? $c : true;
	}

	public static function _list_static($type, $var)
	{
		$c = pps::_chk_type_mthd($type);
		$list = array();
		foreach ($c as $k => $v)
		{
			if (('_' !== $k[0]) && ($var !== is_callable($v)))
			{ $list[] = $k; }
		}
		sort($list);
		return $list;
	}

	public static function _aop_add($where, $type, $mthd, $fun)
	{
		$c = pps::_chk_type_mthd($type, $mthd);
		if (!is_array($c->__methods[$mthd])) // 按需转成数组
		{ $c->__methods[$mthd] = array($c->__methods[$mthd]); }
		$ary = &$c->__methods[$mthd]; // 找到方法在数组中的索引
		$len = count($ary);
		for ($mi=0; $mi<$len; ++$mi)
		{
			if (is_callable($ary[$mi]) && !($ary[$mi] instanceof ppsfun))
			{ break; }
		}
		if (($mi < $len) && ($where < 0))
		{ array_splice($ary, $mi, 0, array($fun)); }
		else
		{ $ary[] = $fun; }
	}
}

class ppsobj
{
	public $__type;    // 类型，指向 pps::$class 中的某一项
	public $__callstk; // 调用栈，用于追踪每个方法的静态类型

	public function __construct($c = null, $agms = null)
	{
		$this->__type = $c->__type;
		if ($c && isset($c->__methods['__init']))
		{ pps::_call_mthd($this, '__init', $c->__type, $c->__methods['__init'], $agms); }
	}

	public function __call($mthd, $agms)
	{
		$c = pps::$class[$this->__type];
		while ($c && !isset($c->__methods[$mthd]))
		{ $c = $c->__base ? pps::$class[$c->__base] : null; }
		if (!$c)
		{ throw new \Exception('类"' . $this->__type . '"的方法"' . $mthd . '()"未定义', -1); }
		return pps::_call_mthd($this, $mthd, $c->__type, $c->__methods[$mthd], $agms);
	}
}

class ppsfun
{
	public $__lambda; // 包装的匿名函数，外界只读该属性
	public $__help = ''; // 内联文档，外界读写该属性
	public $__self = false; // 调用时提供self？仅限本类内部使用

	public function __construct($lambda, $pptys = null)
	{
		$this->__lambda = $lambda;
		if (null !== $pptys)
		{
			foreach ($pptys as $k => $v)
			{ $this->$k = $v; }
			$this->$__self = true;
		}
	}

	public function __invoke($a___ = null) 
	{
		$agms = func_get_args();
		if ($this->__self) { array_unshift($agms, $this); }
		return call_user_func_array($this->__lambda, $agms);
	}

	public function __call($mthd, $agms)
	{
		if (isset($this->$mthd) && is_callable($this->$mthd))
		{ return $agms ? call_user_func_array($this->$mthd, $agms) : call_user_func($this->$mthd); }
		throw new \Exception('"ppsfun::' . $mthd . '()未定义"', -1);
	}
}

?>